/*
 * Albareth - an action roleplaying game.
 * Copyright (C) 2008 Jens Anuth
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 *   02110-1301, USA.
 * 
 * $Id: ItemFactory.java 48 2008-05-17 02:04:48Z jens464 $
*/
package albareth.core.entity;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

import albareth.core.action.SpellHeal;
import albareth.core.action.SpellHealMana;
import albareth.core.action.SpellTownPortal;
import albareth.core.entity.item.AuxiliaryArmor;
import albareth.core.entity.item.BaseArmor;
import albareth.core.entity.item.BasicItemContainer;
import albareth.core.entity.item.Bomb;
import albareth.core.entity.item.Drink;
import albareth.core.entity.item.Food;
import albareth.core.entity.item.Gold;
import albareth.core.entity.item.Jewelry;
import albareth.core.entity.item.Potion;
import albareth.core.entity.item.Scroll;
import albareth.core.entity.item.Weapon;
import albareth.frontend.Tile;
import albareth.frontend.TileCollection;
import frosch.util.FToolkit;
import frosch.util.Range;
import frosch.util.WeightedSet;

public class ItemFactory
{
    private static ItemFactory _instance = null;
    
    private WeightedSet _moneyTemplates = new WeightedSet();
    private WeightedSet _weaponTemplates = new WeightedSet();
    private WeightedSet _armorTemplates = new WeightedSet();
    private WeightedSet _jewelryTemplates = new WeightedSet();
    private WeightedSet _foodTemplates = new WeightedSet();
    private WeightedSet _scrollTemplates = new WeightedSet();
    private WeightedSet _potionTemplates = new WeightedSet();
    
    private WeightedSet _templateDistribution = new WeightedSet();
    
    /** Mapping of all existing items. */
    private HashMap _allItems = new HashMap();
    

    
    private ItemFactory()
    {
        loadItems();
        
        //the random distribution weight of treasure by type:
        _templateDistribution = new WeightedSet();
        _templateDistribution.add(_moneyTemplates,   65);
        _templateDistribution.add(_weaponTemplates, 4);
        _templateDistribution.add(_armorTemplates,  9);
        _templateDistribution.add(_jewelryTemplates, 1);
        _templateDistribution.add(_foodTemplates,    6);
        _templateDistribution.add(_scrollTemplates, 5);
        _templateDistribution.add(_potionTemplates, 10);
    }
    

    public static ItemFactory getInstance()
    {
        if (_instance == null) {
            _instance = new ItemFactory();
        }
        return _instance;
    }

    /**
     * Creates a given number of loot items for the given dungeon level.
     */
    public List createLootItems(int amount, int dungeonLevel)
    {
        List items = new LinkedList();
        
        for (int i = 0; i < amount; i++)
        {
            //roll which kind of item?
            WeightedSet templateSet =
                (WeightedSet)_templateDistribution.randomElement();

            //create and enchant the item appropriate to the dungeon level
            Item item = createRandomItem(templateSet);
            item.enchant(dungeonLevel);
            items.add(item);
        }
        
        return items;
    }
    
    private Item createRandomItem(WeightedSet itemSet)
    {
        Item templateItem = (Item)itemSet.randomElement();
        return cloneItem(templateItem);
        
    }
    
    private Item cloneItem(Item item)
    {
        try {
            return (Item)item.clone();

        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(
                    "Could not clone item " + item.getName() + ": " + e
            );
        }
    }

    public Item createItem(String defname)
    {
        return createItem(defname, 1);
    }
    
    
    public Item createItem(String defname, int amount)
    {
        Item templateItem = (Item)_allItems.get(defname);
        if (templateItem == null) {
            throw new RuntimeException(
                    "Unknown item defname: " + defname
            );
        }
        Item newItem = cloneItem(templateItem);
        newItem.setAmount(amount);
        return newItem;
    }
    
    
    
    
    private void loadItems()
    {
        //gold:
        addItem("i_gold", 1,
                new Gold(1));
        
        //armor:
        addItem("i_mantle", 1,
                new BaseArmor("Mantle", TileCollection.MANTLE, 9, 0));
        addItem("i_robe", 1,
                new BaseArmor("Robe", TileCollection.ROBE, 9, 0));
        addItem("i_padded_shirt", 1,
                new BaseArmor("Padded Shirt", TileCollection.PADDED_SHIRT, 9, 0));
        addItem("i_leather_vest", 1,
                new BaseArmor("Leather Vest", TileCollection.LEATHER_WEST, 8, 7));      
        addItem("i_leather_tunic", 1,
                new BaseArmor("Leather Tunic", TileCollection.LEATHER_TUNIC, 7, 7));      
        addItem("i_leather_armor", 1,
                new BaseArmor("Leather Armor", TileCollection.LEATHER_ARMOR, 6, 9));      
        addItem("i_studded_vest", 1,
                new BaseArmor("Studded Vest", TileCollection.STUDDED_VEST, 5, 9));      
        addItem("i_chain_mail", 1,
                new BaseArmor("Chain Mail", TileCollection.CHAIN_MAIL, 4, 11));      
        addItem("i_plate_chest", 1,
                new BaseArmor("Plate Chest", TileCollection.PLATE_CHEST, 3, 16));      
        addItem("i_plate_mail", 1,
                new BaseArmor("Plate Mail", TileCollection.PLATE_MAIL, 2, 18));      

        //shields:
        addItem("i_golden_shield", 1,
                new AuxiliaryArmor("Golden Shield", TileCollection.GOLD_SHIELD, PlayerCharacter.SLOT_SHIELD, 1, 8));      
        addItem("i_buckler", 1,
                new AuxiliaryArmor("Buckler", TileCollection.BUCKLER, PlayerCharacter.SLOT_SHIELD, 1, 7));
        addItem("i_round_shield", 1,
                new AuxiliaryArmor("Round Shield", TileCollection.ROUND_SHIELD, PlayerCharacter.SLOT_SHIELD, 2, 8));
        addItem("i_leather_shield", 1,
                new AuxiliaryArmor("Leather Shield", TileCollection.LEATHER_SHIELD, PlayerCharacter.SLOT_SHIELD, 2, 8));      
        addItem("i_kite_shield", 1,
                new AuxiliaryArmor("Kite Shield", TileCollection.KITE_SHIELD, PlayerCharacter.SLOT_SHIELD, 3, 11));      
        addItem("i_black_shield", 1,
                new AuxiliaryArmor("Blackened Shield", TileCollection.KITE_SHIELD_DARK, PlayerCharacter.SLOT_SHIELD, 3, 11));      
        addItem("i_spiked_shield", 1,
                new AuxiliaryArmor("Spiked Shield", TileCollection.SPIKED_SHIELD, PlayerCharacter.SLOT_SHIELD, 4, 13));      
        addItem("i_heater_shield", 1,
                new AuxiliaryArmor("Heater Shield", TileCollection.HEATER_SHIELD, PlayerCharacter.SLOT_SHIELD, 4, 14));      
        addItem("i_body_shield", 1,
                new AuxiliaryArmor("Body Shield", TileCollection.BODY_SHIELD, PlayerCharacter.SLOT_SHIELD, 5, 16));     
        addItem("i_knight_shield", 1,
                new AuxiliaryArmor("Knight Shield", TileCollection.KNIGHT_SHIELD, PlayerCharacter.SLOT_SHIELD, 5, 16));      

        //helmets:
        addItem("i_crown", 1,
                new AuxiliaryArmor("Crown", TileCollection.CROWN, PlayerCharacter.SLOT_HELMET, 0, 0));  
        addItem("i_cap", 1,
                new AuxiliaryArmor("Cap", TileCollection.CAP, PlayerCharacter.SLOT_HELMET, 0, 0));       
        addItem("i_hood", 1,
                new AuxiliaryArmor("Hood", TileCollection.HOOD, PlayerCharacter.SLOT_HELMET, 0, 0));     
        addItem("i_cloth_Hat", 1,
                new AuxiliaryArmor("Cloth Hat", TileCollection.CLOTH_HAT, PlayerCharacter.SLOT_HELMET, 0, 0));
        addItem("i_wizards_hat", 1,
                new AuxiliaryArmor("Wizard's Hat", TileCollection.WIZARDS_HAT, PlayerCharacter.SLOT_HELMET, 0, 0));
        addItem("i_leather_cap", 1,
                new AuxiliaryArmor("Leather Cap", TileCollection.LEATHER_CAP, PlayerCharacter.SLOT_HELMET, 1, 0));
        addItem("i_light_helmet", 1,
                new AuxiliaryArmor("Light Helmet", TileCollection.LIGHT_HELMET, PlayerCharacter.SLOT_HELMET, 2, 8));
        addItem("i_helmet", 1,
                new AuxiliaryArmor("Helmet", TileCollection.HELMET, PlayerCharacter.SLOT_HELMET, 3, 9));  
        addItem("i_closed_helmet", 1,
                new AuxiliaryArmor("Closed Helmet", TileCollection.CLOSED_HELMET, PlayerCharacter.SLOT_HELMET, 4, 11));
        addItem("i_knight_helmet", 1,
                new AuxiliaryArmor("Knight Helmet", TileCollection.KNIGHT_HELMET, PlayerCharacter.SLOT_HELMET, 5, 15));
        addItem("i_hornded_helmet", 1,
                new AuxiliaryArmor("Horned Helmet", TileCollection.HORNED_HELMET, PlayerCharacter.SLOT_HELMET, 5, 15));

        //jewelry
        addItem("i_amulet_silver", 2,
                new Jewelry("Amulet", TileCollection.SILVER_AMULETTE, PlayerCharacter.SLOT_AMULETTE));  
        addItem("i_amulet_gold", 2,
                new Jewelry("Amulet", TileCollection.GOLD_AMULETTE, PlayerCharacter.SLOT_AMULETTE));    
        addItem("i_talisman", 1,
                new Jewelry("Talisman", TileCollection.TALISMAN, PlayerCharacter.SLOT_AMULETTE));
        addItem("i_necklace", 1,
                new Jewelry("Necklace", TileCollection.NECLACE, PlayerCharacter.SLOT_AMULETTE));          
        addItem("i_ring_silver", 4,
                new Jewelry("Ring", TileCollection.SILVER_RING, PlayerCharacter.SLOT_RING));      
        addItem("i_ring_gold", 2,
                new Jewelry("Ring", TileCollection.GOLD_RING, PlayerCharacter.SLOT_RING));        

        //food
        addItem("i_bread", 1,
                new Food("Bread", TileCollection.BREAD, 3, 20, 10000));       
        addItem("i_potatoe", 1,
                new Food("Potatoe", TileCollection.POTATOE, 3, 20, 10000));     
        addItem("i_apple", 1,
                new Food("Apple", TileCollection.APPLE, 3, 20, 10000));       
        addItem("i_wine", 3,
                new Drink("Wine", TileCollection.WINE, 3, 20, 10000));        
        addItem("i_beer", 3,
                new Drink("Beer", TileCollection.BEER, 3, 20, 10000));        
        addItem("i_leg_of_lamb", 1,
                new Food("Leg of Lamb", TileCollection.LEG_OF_LAMB, 3, 20, 10000)); 
        addItem("i_cheese", 1,
                new Food("Cheese", TileCollection.CHEESE, 3, 20, 10000));      
        addItem("i_roast_beef", 1,
                new Food("Roast Beef", TileCollection.ROAST_BEEF, 3, 20, 10000));  
        addItem("i_pumpkin", 1,
                new Food("Pumpkin", TileCollection.PUMPKIN, 3, 20, 10000));     
        addItem("i_grapes", 1,
                new Food("Grapes", TileCollection.GRAPES, 3, 20, 10000));      
        addItem("i_fish", 1,
                new Food("Fish", TileCollection.FISH, 3, 20, 10000));        

        //scrolls
        addItem("i_scroll_town_portal", 1,
                new Scroll(TileCollection.SCROLL_BLANK, new SpellTownPortal()));
        
        //potions
        addItem("i_potion_heal", 1,
                new Potion(TileCollection.POTION_YELLOW, new SpellHeal()));
        addItem("i_potion_mana", 1,
                new Potion(TileCollection.POTION_BLUE, new SpellHealMana()));
        addItem("i_bomb", 1,
                new Bomb());


        //weapons
        addItem("i_sword", 1,
                new Weapon("Sword", TileCollection.SWORD, new Range(4, 8), 700, 9, 8, false));
        addItem("i_claymore", 1,
                new Weapon("Claymore", TileCollection.CLAYMORE, new Range(8, 14), 1300, 16, 8, true));          
        addItem("i_morning_star", 1,
                new Weapon("Morning Star", TileCollection.MORNING_STAR, new Range(5, 10), 900, 10, 6, false));      
        addItem("i_battle_axe", 1,
                new Weapon("Battle Axe", TileCollection.BATTLE_AXE, new Range(4, 9), 825, 11, 6, false));       
        addItem("i_double_axe", 1,
                new Weapon("Double Axe", TileCollection.DOUBLE_AXE, new Range(7, 13), 1150, 13, 6, true));        
        addItem("i_serpentine_sword", 1,
                new Weapon("Serpentine Sword", TileCollection.SERPENTINE_SWORD, new Range(2, 10), 700, 7, 12, false));  
        addItem("i_war_hammer", 1,
                new Weapon("War Hammer", TileCollection.WAR_HAMMER, new Range(5, 9), 875, 12, 7, false));        
        addItem("i_dagger", 1,
                new Weapon("Dagger", TileCollection.DAGGER, new Range(1, 6), 425, 0, 0, false));            
        addItem("i_quarterstaff", 1,
                new Weapon("Quarterstaff", TileCollection.QUARTERSTAFF, new Range(3, 6), 400, 7, 0, true));      
        addItem("i_crook", 1,
                new Weapon("Crook", TileCollection.CROOK, new Range(1, 6), 600, 0, 0, true));
        addItem("i_broad_sword", 1,
                new Weapon("Broad Sword", TileCollection.BROAD_SWORD, new Range(5, 9), 825, 10, 7, false));
        addItem("i_halberd", 1,
                new Weapon("Halberd", TileCollection.HALBERD, new Range(10, 14), 2000, 15, 7, true));
        addItem("i_spear", 1,
                new Weapon("Spear", TileCollection.SPEAR, new Range(5, 14), 1100, 8, 7, true));
        addItem("i_trident", 1,
                new Weapon("Trident", TileCollection.TRIDENT, new Range(6, 14), 1200, 8, 10, true));
        addItem("i_mace", 1,
                new Weapon("Mace", TileCollection.MACE, new Range(3, 9), 800, 10, 6, false));
        addItem("i_falchion", 1,
                new Weapon("Falchion", TileCollection.FALCHION, new Range(1, 8), 625, 7, 7, false));
        addItem("i_gnarled_staff", 1,
                new Weapon("Gnarled Staff", TileCollection.GNARLED_STAFF, new Range(1, 7), 550, 0, 0, true));
        addItem("i_glaive", 1,
                new Weapon("Glaive", TileCollection.GLAIVE, new Range(6, 14), 1150, 10, 7, true));
        addItem("i_scythe", 1,
                new Weapon("Scythe", TileCollection.SCYTHE, new Range(6, 14), 1150, 8, 9, true));
        addItem("i_hand_axe", 1,
                new Weapon("Hand Axe", TileCollection.HAND_AXE, new Range(3, 8), 700, 7, 6, false));
        addItem("i_whip", 1,
                new Weapon("Whip", TileCollection.WHIP, new Range(1, 6), 600, 0, 10, false));
        addItem("i_flail", 1,
                new Weapon("Flail", TileCollection.FLAIL, new Range(1, 10), 800, 7, 6, false));
        addItem("i_pick", 1,
                new Weapon("Pick", TileCollection.PICK, new Range(2, 7), 800, 7, 5, false));
        addItem("i_bladed_staff", 1,
                new Weapon("Bladed Staff", TileCollection.BLADED_STAFF, new Range(3, 10), 300, 7, 18, true));

    }

    
    public void addItem(String defname, int weight, Item item)
    {
        if (_allItems.containsKey(defname)) {
            throw new RuntimeException("Duplicate defname: " + defname);
        }
        _allItems.put(defname, item);
        
        WeightedSet relevantSet = null;
        if (item instanceof Gold) {
            relevantSet = _moneyTemplates;
        } else if (item instanceof Weapon) {
            relevantSet = _weaponTemplates;
        } else if (item instanceof BaseArmor || item instanceof AuxiliaryArmor) {
            relevantSet = _armorTemplates;
        } else if (item instanceof Jewelry) {
            relevantSet = _jewelryTemplates;
        } else if (item instanceof Food || item instanceof Drink) {
            relevantSet = _foodTemplates;
        } else if (item instanceof Scroll) {
            relevantSet = _scrollTemplates;
        } else if (item instanceof Potion  || item instanceof Bomb) {
            relevantSet = _potionTemplates;
        } else {
            throw new RuntimeException("No set defined for " + item.getClass());
        }
        relevantSet.add(item, weight);
    }
    



    public static Item createBlood()
    {
        Item blood = new Item("Blood", TileCollection.BLOOD_SMEAR);
        blood.setLockedDown(true);
        return blood;
    }

    public static BasicItemContainer createDestroyableContainer(
            String name, Tile tile)
    {
        BasicItemContainer container = createContainer(name, tile);
        container.setDestroyable(true);
        container.setActivatable(false);
        return container;
    }
    
    public static BasicItemContainer createContainer(String name, Tile tile)
    {
        BasicItemContainer container = new BasicItemContainer(name, tile);
        container.setLockedDown(true);
        container.setActivatable(true);
        container.setBlocking(true);
        container.setCapacity(8);
        return container;
    }
    
    public static BasicItemContainer createRandomContainer()
    {
        switch (FToolkit.rand(3))
        {
            case 0:
                return createContainer("Metal Chest", TileCollection.CHEST_BLACK);
            case 1:
                return createContainer("Wooden Chest", TileCollection.CHEST_RED);
            default:
                return createContainer("Crate", TileCollection.CRATE);
        }
    }

    public Food createRandomFood()
    {
        return (Food)createRandomItem(_foodTemplates);
    }
    

    



}
